/**
 * Copyright (c) 2022 Presto Labs Pte. Ltd.
 * Author: Ju
 */

#include "cc/coin2/exchange/base/order/stats/stats_manager.h"

#include <glog/logging.h>
#include <unistd.h>

namespace coin2::exchange::base::order {

namespace {
static const std::string host = "35.79.11.23";
static const std::string org = "order";
static const int port = 8086;
static const std::string db = "order";
static const std::string token =
    "d37FqY39jpcxuSmthcMjmBnbCsWiwxk0yjpkLPjWzIANgY7ro5iNpuN-"
    "Hwcg3JObKFJpPPIBWito_yLVt4hkWA==";
static const uint64_t kReportInterval = 30'000'000'000LL;
}  // namespace

StatsManager::StatsManager(boost::asio::io_context* ioc,
                           boost::asio::io_context* main_ioc,
                           Callback* callback, const std::string& strategy_name)
    : ioc_(ioc),
      main_ioc_(main_ioc),
      callback_(callback),
      timer_(new Scheduler(ioc_)),
      client_(new influxdb::InfluxdbClient(ioc_, timer_, host, org, port, db,
                                           token)),
      sys_info_(ioc_, timer_),
      strategy_name_(strategy_name) {
  char local_host_name[256];
  std::string host_name;
  if (gethostname(local_host_name, 256)) {
    LOG(ERROR) << "Get Host Name Error!";
    host_name = "default";
  } else {
    host_name = local_host_name;
  }
  sys_info_builder_.Measurement("sys_info")
      .Tag("host", host_name)
      .Tag("strategy_name", strategy_name);
  sys_info_builder_.Freeze();
}

StatsManager::~StatsManager() { timer_->Stop(); }

void StatsManager::Run() {
  timer_->AddPeriodicTask(std::bind(&StatsManager::DoReport, this),
                          kReportInterval, "time_report");

  client_->Run();

  timer_->Start();
}

void StatsManager::DoReport() {
  boost::asio::post(*main_ioc_, [this]() { callback_->OnGenricReport(); });

  SysInfoReport();
}

void StatsManager::SysInfoReport() {
  SelfMemInfo info;
  info.size = 0;
  GetSelfMemInfo(&info);
  sys_info_builder_.TypeField("memory", info.size);
  sys_info_builder_.TypeField("cpu", sys_info_.GetCpuUsage());
  client_->SendData(sys_info_builder_.Build());
}

void StatsManager::GenericReport(const std::string& system_name, const std::string& mea, Info info) {
  boost::asio::post(*ioc_, [this, system_name, mea, info]() {
      auto req_info = info.req_info;
      for (auto& pair : req_info) {
        auto method = pair.first.first;
        auto end_point = pair.first.second;

        auto iter = builders_.find(mea + method + end_point);
        influxdb::Builder builder;
        if (iter == builders_.end()) {
          RegisterEndPointBuilder(mea, method, end_point);
        }
        builder = builders_.at(mea + method + end_point);
        for (auto& http_code_info : pair.second.http_code) {
          builder.TypeField(std::to_string(http_code_info.first),
                            http_code_info.second);
        }
        client_->SendData(builder.Build());
      }
      auto iter = builders_.find(system_name);
      if (iter != builders_.end()) {
        auto builder = iter->second;
        auto order_info = info.stats_info;
        auto rtt_info = info.rtt_info;
        auto ws_reset_num = info.ws_reset_num;
        sort(rtt_info.free_session_size.begin(),rtt_info.free_session_size.end());
        sort(rtt_info.work_session_size.begin(),rtt_info.work_session_size.end());
        builder.TypeField("order_reject_num", order_info.order_reject_num);
        builder.TypeField("order_accept_num", order_info.order_accept_num);
        builder.TypeField("cancel_reject_num", order_info.cancel_reject_num);
        builder.TypeField("req_rtt_200ms", rtt_info.rtt_point[0]);
        builder.TypeField("req_rtt_500ms", rtt_info.rtt_point[1]);
        builder.TypeField("req_rtt_1000ms", rtt_info.rtt_point[2]);
        builder.TypeField("req_rtt_5000ms", rtt_info.rtt_point[3]);
        builder.TypeField("req_rtt_timeout", rtt_info.rtt_point[4]);
        builder.TypeField("req_num", rtt_info.req_num);
        builder.TypeField("rep_num", rtt_info.rep_num);
        builder.TypeField("err_num", rtt_info.err_num);
        builder.TypeField("ws_reset_num", ws_reset_num);
        
        // 0 50 100
        auto size = rtt_info.work_session_size.size();
        if (size) {
            builder.TypeField("work_size_0", rtt_info.work_session_size[0]);
            builder.TypeField("work_size_50", rtt_info.work_session_size[size/2]);
            builder.TypeField("work_size_100", rtt_info.work_session_size[size-1]);
        }

        size = rtt_info.free_session_size.size();
        if (size) {
            builder.TypeField("free_size_0", rtt_info.free_session_size[0]);
            builder.TypeField("free_size_50", rtt_info.free_session_size[size/2]);
            builder.TypeField("free_size_100", rtt_info.free_session_size[size-1]);
        }
        client_->SendData(builder.Build());
      }
  });
}

void StatsManager::RegisterBuilder(const std::string& sys_name,
                                   const std::string& mea,
                                   const std::string& owner) {
  influxdb::Builder builder;
  builder.Measurement("order").Tag("mea", mea).Tag("owner", owner).Tag("strategy_name", strategy_name_);
  builder.Freeze();
  builders_.emplace(std::make_pair(sys_name, builder));
}

void StatsManager::RegisterEndPointBuilder(
    const std::string& mea,
    const std::string& method,
    const std::string& target) {
  influxdb::Builder builder;
  builder.Measurement("req_info")
      .Tag("mea", mea)
      .Tag("strategy_name", strategy_name_)
      .Tag("method", method)
      .Tag("target", target);
  builder.Freeze();
  builders_.emplace(std::make_pair(mea + method + target, builder));
}

}  // namespace coin2::exchange::base::order
